package server;

import container.MyContext;
import container.MyHost;
import container.MyWrapper;
import container.impl.MyMappedContext;
import container.impl.MyMappedHost;
import container.impl.MyMappedWrapper;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

public class MyBootstrap {

    //定义监听的端口
    private Integer port = 8080;

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    private Map<String, MyHttpServlet> urlToMyHttpServletMap = new HashMap<>();

    //项目路径
    private String webappsPath = "";

    //host : 127.0.0.1  context : demo   wrapper : /lagou
    private Map<String, Map<String, Map<String, MyHttpServlet>>> hosts = new HashMap<>();

    //key:hostName 127.0.0.1 value Myhost
    private Map<String, MyHost> hostNameToHostMap = new HashMap<>();

    /**
     * Minicat启动需要初始化展开的操作
     */
    public void start() throws Exception {

        //初始化servlet
        loadServletByMany();

        // 定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize = 50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();


        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );

        /**
         * 浏览器请求http://localhost:8080,返回⼀个固定的字符串到⻚⾯"Hello Minicat!"
         */

        //１.创建一个ServerSocket实例，监听客户端发来的请求。
        //2.与客户端获取连接后，创建一个Socket实例，利用I/O流与客户端进行通信，完毕后关闭Socket
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("============================>>MyMinicat start port: " + port);

        //阻塞式监听port
        /*while (true){
            //接收请求
            Socket socket = serverSocket.accept();
            //获取输出流
            OutputStream out = socket.getOutputStream();
            String data = "I LOVE　You";
            //封装响应头信息
            String httpHeader200 = HttpProtocolUtil.getHttpHeader200(data.length())+data;
            out.write(httpHeader200.getBytes());
            socket.close();
        }*/

        /**
         *V2.0需求：封装Request和Response对象，返回html静态资源⽂件
         */
        //阻塞式监听port
//        while (true){
//            //接收请求
//            Socket socket = serverSocket.accept();
//            //获取输入流
//            InputStream in = socket.getInputStream();
//            Request request = new Request(in);
//            OutputStream outputStream = socket.getOutputStream();
//            Response response = new Response(outputStream);
//            response.outputHtml(request.getUrl());
//            socket.close();
//
//        }

        /**
         *V3.0需求：可以请求动态资源（Servlet）
         * 1.定义servlet 1)定义Servlet接口  2）定义HttpServlet抽象类 实现Servlet接口 3）定义LagouServlet类继承HttpServlet抽象类
         * 2.加载解析servlet:
         * 1)初始化servlet 在web.xml中配置初始化servlet
         * 2)引入dom4依赖 利用dom4j加载解析servlet
         * 3)添加到启动方法 服务启动时加载
         */
        //阻塞式监听
//        while (true){
//            //接收请求
//            Socket socket = serverSocket.accept();
//            //获取输入流
//            InputStream inputStream = socket.getInputStream();
//            //获取输出流
//            OutputStream outputStream = socket.getOutputStream();
//            Response response = new Response(outputStream);
//            //获取url
//            Request request = new Request(inputStream);
//            String url = request.getUrl();
//            //获取对应MyHttpServlet
//            MyHttpServlet myHttpServlet = urlToMyHttpServletMap.get(url);
//            if(myHttpServlet == null){
//                //访问静态资源
//                response.outputHtml(url);
//            }else {
//                //调用MyHttpServlet
//                myHttpServlet.service(request,response);
//            }
//            socket.close();
//        }


        /**
         * 改造优化 1.多线程改造(非线程池)
         */
//        while (true){
//            //接收请求
//            Socket socket = serverSocket.accept();
//            MyRequestProcessor myRequestProcessor = new MyRequestProcessor(socket, urlToMyHttpServletMap);
//            myRequestProcessor.start();
//        }

        /**
         * 改造优化 2.线程池改造
         */
//        System.out.println("=========>>>>>>使用线程池进行多线程改造");
//        while (true) {
//            //接收请求
//            Socket socket = serverSocket.accept();
//            MyRequestProcessor myRequestProcessor = new MyRequestProcessor(socket, urlToMyHttpServletMap);
//            threadPoolExecutor.execute(myRequestProcessor);
//        }


        /***
         * 5.0 多项目部署
         */
//        while(true) {
//            Socket socket = serverSocket.accept();
//            MyRequestProcessor requestProcessorr = new MyRequestProcessor(socket,hosts,webappsPath);
//            threadPoolExecutor.execute(requestProcessorr);
//        }

    }

    public void minicatV4() throws IOException {
        //初始化
        //加载
        load();
        //初始化servlet
        init();

        start4();

    }

    private void start4() throws IOException {
        // 定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize = 50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();


        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );

        //１.创建一个ServerSocket实例，监听客户端发来的请求。
        //2.与客户端获取连接后，创建一个Socket实例，利用I/O流与客户端进行通信，完毕后关闭Socket
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("============================>>MyMinicat start port: " + port);
        /***
         * 5.0 多项目部署
         */
        while(true) {
            Socket socket = serverSocket.accept();
            MyRequestProcessor requestProcessorr = new MyRequestProcessor(socket,hostNameToHostMap,webappsPath);
            threadPoolExecutor.execute(requestProcessorr);
        }
    }

    private void init(){
        for (String s : hostNameToHostMap.keySet()) {
            MyHost myHost = hostNameToHostMap.get(s);
            myHost.init();
        }

    }

    private void load(){
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        //解析server.xml
        Document document = null;
        try {
            document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> portNodes = rootElement.selectNodes("//Connect");
            Element element = (Element) rootElement.selectSingleNode("//Connect");
            port = Integer.parseInt(element.attributeValue("port"));

            List<Element> hostsEle = rootElement.selectNodes("//Host");//获取host
            for (Element element1 : hostsEle) {
                //初始化MyHost
                MyHost host = createHost(element1);
                hostNameToHostMap.put(host.getName(),host);
            }

        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    private MyHost createHost(Element element){
        MyHost myHost = new MyMappedHost();
        //设置hostName(访问ip localhost/127.0.0.1)
        String hostName = element.attributeValue("name");
        myHost.setName(hostName);
        //设置appName E:\ws_tomcat\webapps
        String appBase = element.attributeValue("appBase").replaceAll("\\\\", "/");
        myHost.setAppBase(appBase);

        File appBaseFile = new File(appBase);
        if(appBaseFile.exists() && appBaseFile.isDirectory()){
            File[] files = appBaseFile.listFiles();
            for (File file : files) {//file ->web项目
                //初始化MyContext
                MyContext myContext = createMyContext(file);
                myHost.setContexts(myContext);
            }

        }
        return myHost;
    }

    private MyContext createMyContext(File file){
        MyContext context = new MyMappedContext();
        //获取项目路径
        String webPath = file.getPath();
        //单个项目文件名 demo1 demo2
        String fileName = webPath.replace(file.getPath() + "\\", "");
        String name = file.getName();
        context.setContextName(name);

        String pakageDir = webPath.replaceAll("\\\\", "/");
        MyClassLoader loader = new MyClassLoader(pakageDir + "/");
        context.setClassLoad(loader);


        //读取web.xml文件
        File webXml = new File(webPath + "\\web.xml");
        if(webXml.exists()){
            //初始化MyWrapper
            createMyWrapper(context,webXml);
        }
       return context;
    }

    private void createMyWrapper(MyContext context,File webXml){
        try {
            InputStream resourceAsStreamWeb = new FileInputStream(webXml);
            SAXReader saxReaderWeb = new SAXReader();
            Document documentWeb = saxReaderWeb.read(resourceAsStreamWeb);
            Element rootElementWeb = documentWeb.getRootElement();
            List<Element> selectNodes = rootElementWeb.selectNodes("//servlet");
            for (int i = 0; i < selectNodes.size(); i++) {


                Element elementWeb = selectNodes.get(i);
                // <servlet-name>lagou</servlet-name>
                Element servletnameElement = (Element) elementWeb.selectSingleNode("servlet-name");
                String servletName = servletnameElement.getStringValue();
                // <servlet-class>server.LagouServlet</servlet-class>
                Element servletclassElement = (Element) elementWeb.selectSingleNode("servlet-class");

                String servletClass = servletclassElement.getStringValue();//class绝对路径
                // 根据servlet-name的值找到url-pattern
                Element servletMapping = (Element) rootElementWeb.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                //  /myServlet
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                MyWrapper wrapper = new MyMappedWrapper();
                wrapper.setServletName(servletName);
                wrapper.setClassName(servletClass);
                wrapper.setUrlPattern(urlPattern);
                context.setMyWrapper(wrapper);

            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        }

    }


    private void loadServletByMany() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();


        try {
            //解析server.xml
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> portNodes = rootElement.selectNodes("//Connect");
            Element element = (Element) rootElement.selectSingleNode("//Connect");
            port = Integer.parseInt(element.attributeValue("port"));

            List<Element> hostsEle = rootElement.selectNodes("//Host");//获取host

            for (int i = 0; i < hostsEle.size(); i++) {
                String hostName = hostsEle.get(i).attributeValue("name");

                //项目集
                Map<String, Map<String, MyHttpServlet>> contexts = new HashMap<>();
                webappsPath = hostsEle.get(i).attributeValue("appBase");//host对应项目路径路径
                webappsPath = webappsPath.replaceAll("\\\\", "/");

                File file = new File(webappsPath);
                if (file.exists() && !file.isFile()) {

                    File[] files = file.listFiles();
                    for (int j = 0; j < files.length; j++) {
                        //项目
                        Map<String, MyHttpServlet> context = new HashMap<>();
                        String webPath = files[j].getPath();//项目路径
                        //单个项目文件名
                        String fileName = webPath.replace(file.getPath() + "\\", "");
                        //读取web.xml文件
                        File webXml = new File(webPath + "\\web.xml");
                        InputStream resourceAsStreamWeb = new FileInputStream(webXml);
                        SAXReader saxReaderWeb = new SAXReader();


                        Map<String, MyHttpServlet> wrapper = new HashMap<String, MyHttpServlet>();
                        //解析各个项目web.xml
                        try {
                            Document documentWeb = saxReaderWeb.read(resourceAsStreamWeb);
                            Element rootElementWeb = documentWeb.getRootElement();

                            List<Element> selectNodes = rootElementWeb.selectNodes("//servlet");
                            for (int k = 0; k < selectNodes.size(); k++) {


                                Element elementWeb = selectNodes.get(k);
                                // <servlet-name>lagou</servlet-name>
                                Element servletnameElement = (Element) elementWeb.selectSingleNode("servlet-name");
                                String servletName = servletnameElement.getStringValue();
                                // <servlet-class>server.LagouServlet</servlet-class>
                                Element servletclassElement = (Element) elementWeb.selectSingleNode("servlet-class");

                                String servletClass = servletclassElement.getStringValue();//class绝对路径
                                // 根据servlet-name的值找到url-pattern
                                Element servletMapping = (Element) rootElementWeb.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                                // /lagou
                                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
//                                webPath = webPath + "\\WEB-INF\\classes";
                                String pakageDir = webPath.replaceAll("\\\\", "/");
                                MyClassLoader loader = new MyClassLoader(pakageDir + "/");
                                Class<?> clazz = loader.findClass(servletClass);
                                wrapper.put(urlPattern, (MyHttpServlet) clazz.getClassLoader().loadClass(servletClass).newInstance());
                            }

                        } catch (DocumentException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InstantiationException e) {
                            e.printStackTrace();
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }


                        //context.put(fileName,);
                        contexts.put(fileName, wrapper);
                    }


                }

                if ("localhost".equalsIgnoreCase(hostName)) {
                    hosts.put("127.0.0.1", contexts);//增加127.0.0.1 对应 localhost
                }
                hosts.put(hostName, contexts);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }



        /*       */

    }


    private void loadInitServletList() {
        //加载server.xml
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(inputStream);
            Element rootElement = document.getRootElement();


        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    /**
     * 功能描述:加载web.xml解析Servlet
     *
     * @param
     * @return void
     * @author zj
     * @date 2021/7/17
     */
    public void loadInitServlet() {
        //加载web.xml
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(inputStream);
            //获取根标签
            Element rootElement = document.getRootElement();
            List<Element> list = rootElement.selectNodes("//servlet");
            for (Element element : list) {
                Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletNameElement.getStringValue();

                Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletClassElement.getStringValue();

                //根据servletName找到url-pattern
                Element servletMappingElement = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                String urlPattern = servletMappingElement.selectSingleNode("url-pattern").getStringValue();

                urlToMyHttpServletMap.put(urlPattern, (MyHttpServlet) Class.forName(servletClass).newInstance());

            }

        } catch (DocumentException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        MyBootstrap myBootstrap = new MyBootstrap();
        try {
            myBootstrap.minicatV4();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
